package it.uniroma2.editor.extension;

import it.uniroma2.editor.WorkflowEditor;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Hashtable;

import javax.swing.ImageIcon;

import org.w3c.dom.Document;

import com.mxgraph.io.mxCodec;
import com.mxgraph.model.mxICell;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxCellOverlay;
import com.mxgraph.swing.util.mxICellOverlay;
import com.mxgraph.util.mxResources;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;

public class CustomGraphComponent extends mxGraphComponent{
	
	private static final long serialVersionUID = 1L;
	
	
	public CustomGraphComponent(mxGraph graph)
	{
		super(graph);

		// Sets switches typically used in an editor
		setPageVisible(true);
		setGridVisible(true);
		setToolTips(true);
		getConnectionHandler().setCreateTarget(false);

		// Loads the defalt stylesheet from an external file
		mxCodec codec = new mxCodec();
		Document doc = mxUtils.loadDocument(WorkflowEditor.class.getResource(
				"/com/mxgraph/examples/swing/resources/default-style.xml")
				.toString());
		codec.decode(doc.getDocumentElement(), graph.getStylesheet());

		// Sets the background to white
		getViewport().setOpaque(true);
		getViewport().setBackground(Color.WHITE);
	}

	/**
	 * Overrides drop behaviour to set the cell style if the target
	 * is not a valid drop target and the cells are of the same
	 * type (eg. both vertices or both edges). 
	 */
	public Object[] importCells(Object[] cells, double dx, double dy,
			Object target, Point location)
	{
		if (target == null && cells.length == 1 && location != null)
		{
			target = getCellAt(location.x, location.y);

			if (target instanceof mxICell && cells[0] instanceof mxICell)
			{
				mxICell targetCell = (mxICell) target;
				mxICell dropCell = (mxICell) cells[0];

				if (targetCell.isVertex() == dropCell.isVertex()
						|| targetCell.isEdge() == dropCell.isEdge())
				{
					mxIGraphModel model = graph.getModel();
					model.setStyle(target, model.getStyle(cells[0]));
					graph.setSelectionCell(target);

					return null;
				}
			}
		}

		return super.importCells(cells, dx, dy, target, location);
	}
	
		
	@Override
	public mxICellOverlay setCellWarning(Object cell, String warning) {
		return this.setCellWarning(cell, warning, null, false);
	}	
	
	
	@Override
	public mxICellOverlay setCellWarning(Object cell, String warning,
			ImageIcon icon) {
		return this.setCellWarning(cell, warning, icon, false);
	}
	
	
	@Override
	public mxICellOverlay setCellWarning(final Object cell, String warning,
			ImageIcon icon, boolean select) {
		
		
		if (warning != null && warning.length() > 0)
		{
			icon = (icon != null) ? icon : warningIcon;

			// Creates the overlay with the image and warning
			mxCellOverlay overlay = new mxCellOverlay(icon, warning);

			// Adds a handler for single mouseclicks to select the cell
			if (select)
			{
				overlay.addMouseListener(new MouseAdapter()
				{
					/**
					 * Selects the associated cell in the graph
					 */
					public void mousePressed(MouseEvent e)
					{
						if (getGraph().isEnabled())
						{
							getGraph().setSelectionCell(cell);
						}
					}
				});

				overlay.setCursor(new Cursor(Cursor.HAND_CURSOR));
			}

			// Sets and returns the overlay in the graph
			return addCellOverlay(cell, overlay);
		}
		else
		{
			removeCellOverlays(cell);
		}
		return null;
	}
	
	
	@Override
	public String validateGraph() {
		return validateGraph(graph.getModel().getRoot(),
				new Hashtable<Object, Object>());
	}
	
	
	@Override
	public String validateGraph(Object cell, Hashtable<Object, Object> context) {
		mxIGraphModel model = graph.getModel();
		mxGraphView view = graph.getView();
		boolean isValid = true;
		int childCount = model.getChildCount(cell);

		for (int i = 0; i < childCount; i++)
		{
			Object tmp = model.getChildAt(cell, i);
			Hashtable<Object, Object> ctx = context;

			if (graph.isValidRoot(tmp))
			{
				ctx = new Hashtable<Object, Object>();
			}
						
			String warn = validateGraph(tmp, ctx);

			if (warn != null)
			{
				if(model.isEdge(tmp)){
					System.out.println("Arco ");
					String html = warn.replaceAll("\n", "<br>");
					int len = html.length();
					setCellWarning(tmp, html.substring(0, Math.max(0, len - 4)), DEFAULT_WARNING_ICON);
				}else{
					String html = warn.replaceAll("\n", "<br>");
					int len = html.length();
					java.net.URL imgURL = getClass().getResource("image" + File.separator + "danger.gif");
					if (imgURL != null) {
						ImageIcon image = new ImageIcon(imgURL, "");
						setCellWarning(tmp, html.substring(0, Math.max(0, len - 4)), image);
					}
				}
				
			}
			else
			{
				setCellWarning(tmp, null);
			}

			isValid = isValid && warn == null;
		}

		StringBuffer warning = new StringBuffer();

		// Adds error for invalid children if collapsed (children invisible)
		if (graph.isCellCollapsed(cell) && !isValid)
		{
			warning.append(mxResources.get("containsValidationErrors",
					"Contains Validation Errors") + "\n");
		}

		// Checks edges and cells using the defined multiplicities
		if (model.isEdge(cell))
		{
			String tmp = graph.getEdgeValidationError(cell,
					model.getTerminal(cell, true),
					model.getTerminal(cell, false));
			
			System.out.println("ERRORE arco : " + tmp);
			
			
			if (tmp != null)
			{
				warning.append(tmp);
			}
		}
		else
		{
			String tmp = graph.getCellValidationError(cell);
			System.out.println(tmp);
			if (tmp != null)
			{
				warning.append(tmp);
			}
		}

		// Checks custom validation rules
		String err = graph.validateCell(cell, context);

		if (err != null)
		{
			warning.append(err);
		}

		// Updates the display with the warning icons before any potential
		// alerts are displayed
		if (model.getParent(cell) == null)
		{
			view.validate();
		}

		return (warning.length() > 0 || !isValid) ? warning.toString() : null;
	}
	
	@Override
	
	public mxGraph getGraph() {
		return this.graph;
	}
}
